#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <pthread.h>
#include <mutex>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "log.hpp"
#include "tcp_error.h"

using std::cerr;
using std::cout;
using std::endl;

using func_t = std::function<std::string(const std::string &)>;
const int g_default_port = 8081;
const int backlog = 32;

class TcpServer;
class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *ts)
        : sock(fd), clientip(ip), clientport(port), ts_(ts)
    {
    }

public:
    int sock;
    std::string clientip;
    uint16_t clientport;
    TcpServer *ts_;
};

class TcpServer
{
public:
    TcpServer(func_t func, uint16_t port = g_default_port)
        : port_(port), func_(func)
    {
    }
    void init_service()
    {
        // 创建
        listen_sock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sock_ == -1)
        {
            logMessage(Fatal, "create socket error, code: %d, error string: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }

        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = htonl(INADDR_ANY); //也可以不加htonl函数，\
        因为#define INADDR_ANY	((in_addr_t) 0x00000000),意味着这个字段在任何序列下都是0，不存在大小端转换

        if (bind(listen_sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(Fatal, "bind socket error, code: %d, error string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }

        if (listen(listen_sock_, backlog) < 0)
        {
            logMessage(Fatal, "listen error, code: %d, error string: %s", errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
        logMessage(Debug, "listen success");
    }

    static void *threadRoutine(void *args)
    {
        pthread_detach(pthread_self()); // 防止在start_service处阻塞

        ThreadData *td = static_cast<ThreadData *>(args);
        td->ts_->service(td->sock, td->clientip, td->clientport);
        delete td;
        return nullptr;
    }

    void start_service()
    {
        while (true)
        {
            struct sockaddr_in client_addr;
            socklen_t client_addrlen = sizeof(client_addr);
            int connfd = accept(listen_sock_, (struct sockaddr *)&client_addr, &client_addrlen);
            if (connfd < 0)
            {
                logMessage(Warning, "accept error, code: %d, error string: %s", errno, strerror(errno));
                continue;
            }

            // 获取client端信息
            std::string client_ip = inet_ntoa(client_addr.sin_addr); // 网络序列转字符串
            uint16_t client_port = ntohs(client_addr.sin_port);

            std::cout << "accept success" << connfd << " from " << listen_sock_ << ", "
                      << client_ip << "-" << client_port << std::endl;
            logMessage(Info, "获取新连接成功: %d from %d, who: %s - %d",
                       connfd, listen_sock_, client_ip.c_str(), client_port);

            // 多个线程提供服务
            // 传入线程数据类型来访问threadRoutine，因为该函数是static的，所以内部传入了data类型存了tcpserver类型
            pthread_t tid;
            ThreadData *td = new ThreadData(connfd, client_ip, client_port, this);
            pthread_create(&tid, nullptr, threadRoutine, td);
        }
    }

    // 客户端提供消息，服务器给原封不动转回去，就像echo
    void service(int sock, const std::string &client_ip, const uint16_t &client_port)
    {
        std::string who = client_ip + "-" + std::to_string(client_port);
        char buf[1024];

        while (true)
        {
            ssize_t r_ret = read(sock, buf, sizeof(buf));
            if (r_ret > 0)
            {
                buf[r_ret] = 0;
                std::cout << who << func_(buf) << std::endl;
                std::string res = func_(buf); // 进行回调
                logMessage(Debug, "%s# %s", who.c_str(), res.c_str());

                ssize_t w_ret = write(sock, buf, r_ret);
            }
            else if (r_ret < 0)
            {
                close(sock);
                logMessage(Error, "read error, %d:%s", errno, strerror(errno));
                exit(READ_ERR);
            }
            else
            {
                close(sock);
                // read返回值为0说明对端关闭连接。
                logMessage(Info, "%s quit,me too", who.c_str());
                break;
            }
        }
    }

    ~TcpServer() {}

private:
    int listen_sock_;
    uint16_t port_;
    func_t func_;
};